package com.mytrip.aop;

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import com.newheight.annotation.reqcache.ReqCache;
import com.newheight.util.reqcache.ReqCacheUtil;
import com.newheight.util.reqcache.ReqCacheable;

@Aspect
public class ReqCacheAspect extends BaseAspect{

	protected final Log log = LogFactory.getLog(ReqCacheAspect.class);

	@Pointcut("execution(* com.newheight.service..impl.*ManagerImpl.*(..))")
	public void reqCachePt(){}

	@Around("reqCachePt()")
	public Object reqCacheAdvice(ProceedingJoinPoint pjp) throws Throwable{
		Method method = ((MethodSignature) pjp.getSignature()).getMethod();

		if (!method.isAnnotationPresent(ReqCache.class)) {
			return pjp.proceed();
		}

		ReqCache reqCache = method.getAnnotation(ReqCache.class);
		ReqCache.Operate op = reqCache.operate();

		if (op.equals(ReqCache.Operate.GET_AND_PUT)) {
			return dealGetAndPut(pjp, reqCache);
		} else if (op.equals(ReqCache.Operate.UPDATE)) {
			return dealUpdate(pjp, reqCache);
		} else if (op.equals(ReqCache.Operate.UPDATE_BY_RESULT)) {
			return dealUpdateByResult(pjp, reqCache);
		} else if (op.equals(ReqCache.Operate.DELETE)) {
			return dealDelete(pjp, reqCache);
		} else if (op.equals(ReqCache.Operate.DELETE_BY_RESULT)) {
			return dealDeleteByResult(pjp, reqCache);
		}
		return pjp.proceed();
	}

	private Object dealGetAndPut(ProceedingJoinPoint pjp, ReqCache reqCache) throws Throwable{
		String attrKey = reqCache.attrKey();
		int[] keyIdxs = reqCache.keyIdxs();
		Object[] args = pjp.getArgs();

		String mapKey = null;
		try {
			mapKey = getMapKey(attrKey, keyIdxs, args);
		} catch (Exception e) {
			log.error("::", e);
			return pjp.proceed();
		}

		Object res = ReqCacheUtil.get(reqCache.attrKey(), mapKey);
		if (res == null) {
			res = pjp.proceed();
			ReqCacheUtil.put(attrKey, mapKey, res);
		}
		return res;
	}

	private Object dealUpdate(ProceedingJoinPoint pjp, ReqCache reqCache) throws Throwable{
		Object res = pjp.proceed();
		String attrKey = reqCache.attrKey();
		int[] keyIdxs = reqCache.keyIdxs();
		Object[] args = pjp.getArgs();

		for (int keyIdx : keyIdxs) {
			if (keyIdx >= args.length) {
				log.error("keyIdx >= args.length");
				return pjp.proceed();
			}
			Object obj = args[keyIdx];
			if (obj == null) {
				log.error("obj == null");
				return pjp.proceed();
			}
			if (!"".equals(attrKey)) {
				String mapKey = getMapKey(attrKey, obj);
				ReqCacheUtil.put(attrKey, mapKey, obj);
			} else {
				if (res instanceof ReqCacheable) {
					((ReqCacheable) res).freshReqCache();
				}
			}
		}
		return res;
	}

	private Object dealUpdateByResult(ProceedingJoinPoint pjp, ReqCache reqCache) throws Throwable{
		Object res = pjp.proceed();
		if (res != null) {
			String attrKey = reqCache.attrKey();
			if (!"".equals(attrKey)) {
				String mapKey = getMapKey(attrKey, res);
				ReqCacheUtil.put(attrKey, mapKey, res);
			} else {
				if (res instanceof ReqCacheable) {
					((ReqCacheable) res).freshReqCache();
				}
			}
		}
		return res;
	}

	private Object dealDelete(ProceedingJoinPoint pjp, ReqCache reqCache) throws Throwable{
		Object res = pjp.proceed();
		String attrKey = reqCache.attrKey();
		int[] keyIdxs = reqCache.keyIdxs();
		Object[] args = pjp.getArgs();
		for (int keyIdx : keyIdxs) {
			if (keyIdx >= args.length) {
				log.error("keyIdx >= args.length");
				return pjp.proceed();
			}
			Object obj = args[keyIdx];
			if (obj == null) {
				log.error("obj == null");
				return pjp.proceed();
			}
			if (!"".equals(attrKey)) {
				String mapKey = getMapKey(attrKey, obj);
				ReqCacheUtil.del(attrKey, mapKey);
			} else {
				if (res instanceof ReqCacheable) {
					((ReqCacheable) res).clearReqCache();
				}
			}
		}
		return res;
	}

	private Object dealDeleteByResult(ProceedingJoinPoint pjp, ReqCache reqCache) throws Throwable{
		Object res = pjp.proceed();
		if (res != null) {
			String attrKey = reqCache.attrKey();
			if (!"".equals(attrKey)) {
				String mapKey = getMapKey(attrKey, res);
				ReqCacheUtil.del(attrKey, mapKey);
			} else {
				if (res instanceof ReqCacheable) {
					((ReqCacheable) res).clearReqCache();
				}
			}
		}
		return res;
	}

	private String getMapKey(String attrKey, int[] keyIdxs, Object[] args){
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < keyIdxs.length; i++) {
			int keyIdx = keyIdxs[i];
			if (keyIdx >= args.length) {
				throw new RuntimeException("keyIdx >= args.length");
			}
			Object obj = args[keyIdx];
			if (obj == null) {
				throw new RuntimeException("obj == null");
			}
			String _mapKey = getMapKey(attrKey, obj);
			sb.append(_mapKey);
			if (i < keyIdxs.length - 1) {
				sb.append(ReqCacheUtil.REQ_CACHE_SPLIT);
			}
		}
		String mapKey = sb.toString();
		return mapKey;
	}

	private String getMapKey(String attrKey, Object obj){
		return (obj instanceof ReqCacheable) ? ((ReqCacheable) obj).getReqCacheKey(attrKey) : (obj + "");
	}

}
